#include "crossbit/TCache.h"
#include "crossbit/HashMap.h"
#include "crossbit/MemoryManager.h"

using namespace crossbit;

TCache:: TCache( XTUint32 size, MemoryManager *mm ) : tc_size(size), tc_mm(mm), tc_map(mm), tc_pool_id(0) ,tc_pool_state_id(0)
{
	tb_total = ((tc_size/avginblk) & 0xFFFFF400U) + 0x00000400U;
	
	XTUint32 tb_state_total = (tb_total >> 5);
	XTUint32 tb_state_grp_total = (tb_state_total >> 5);
	

	XTUint32 mm_tb_size = tb_total * sizeof(TBlock);
	XTUint32 mm_tb_states_size = (tb_state_total + tb_state_grp_total) * sizeof(XTUint32);

	tb_top = new ((TBlock *) tc_mm->allocateObjectPool(&tc_pool_id, mm_tb_size, mm_tb_size)) TBlock[appnum];
	
	tb_state_grp = new ((XTUint32 *) tc_mm->allocateObjectPool(&tc_pool_state_id, mm_tb_states_size, mm_tb_states_size)) XTUint32[mm_tb_states_size]();

	tb_state = tb_state_grp + tb_state_grp_total;
	tb_cur_avail = appnum;
	tb_cur_init = appnum;

	tc_tcode = tc_mm->getInstance()->allocateExecutable((XTUint32)(tc_size * 1.2));	
	tc_cur_tcode = tc_tcode;
}

TCache::~TCache() 
{
	delete &tc_map;
}

// Begin create a new TBlock
// Return pointer to TBlock.
TBlock* TCache::requestTBlock()
{
	if( tc_cur_tcode >= tc_tcode + tc_size )
					replacePolicy();
	if(!tb_cur_avail)
	{	
		if (tb_cur_init >= tb_total)
		{
			UTIL::XTLOG("TBlock objects are used up!\n");
			return NULL;
		}
		TBlock *tb_tmp = new (tb_top + tb_cur_init) TBlock[appnum];
		*(tb_state + (tb_cur_init >> 5)) = 0x80000000U;
		tb_cur_init += appnum;
		tb_tmp->tb_tcode = tc_cur_tcode;
		tb_cur_avail = appnum - 1;
		return tb_tmp;		
	}
	
	XTUint32 grp_pos = 0;		
	for ( ; *(tb_state_grp + grp_pos) == 0xFFFFFFFFU; grp_pos++);
	XTUint32 mid_pos = 0;
	XTUint32 tmp_state = *(tb_state_grp + grp_pos);
	while (tmp_state >= 0x80000000U)
	{
		tmp_state <<= 1;
		mid_pos++;
	}
			
	XTUint32 pos = 0;
	XTUint32 *tmp_avail_state = tb_state + (grp_pos << 5)  + mid_pos;
	tmp_state = *tmp_avail_state;	

	while (tmp_state >= 0x80000000U)
	{
		tmp_state <<= 1;
		pos++;
	}
				
	*tmp_avail_state |= (0x80000000U >> pos);
	if (*tmp_avail_state == 0xFFFFFFFFU)
				*(tb_state_grp + grp_pos) |= (0x80000000U >> mid_pos);
		
	pos += ((grp_pos << 10) + (mid_pos << 5));

	(tb_top + pos)->tb_tcode = tc_cur_tcode;
	
	tb_cur_avail--;
	return (tb_top + pos);
				
}

// End crreate the TBlock "tblock"
void TCache::submitTBlock(XTMemAddr spc, TBlock *tblock, VBlock *vblock)
{
	tblock->setSPC(spc);
	tc_map.InsertTBlock(spc,tblock);
	tc_map.InsertVBlock(spc,vblock);
	tc_cur_tcode += tblock->tb_size;
	tc_cur_tcode = (tc_cur_tcode + 7 ) & 0xFFFFFFF8U;
}

// Lookup the TBlock from TCache with *Source PC*
TBlock *TCache::lookupTBlock(XTMemAddr spc)
{
	return tc_map.FindTBlock(spc);
}

VBlock *TCache::lookupVBlock(XTMemAddr spc)
{
	return tc_map.FindVBlock(spc);
}

void TCache::eraseTBlock(TBlock *tblock)
{
			
	XTUint32 g_pos = tblock - tb_top;
	XTUint32 *tmp_state = tb_state + (g_pos >> 5);
	XTUint32 pos = (g_pos & 0x1FU);
	
	
	if (*tmp_state == 0xFFFFFFFFU)
	{
		XTUint32 grp_pos = (g_pos >> 10);
		XTUint32 mid_pos = ((g_pos & 0x3FFU) >> 5);
		*(tb_state_grp + grp_pos) &= ~(0x80000000U >> mid_pos);
	}

	*tmp_state &= ~(0x80000000U >> pos);
	tb_cur_avail++;
}
